﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: ObjectInfo.cs
//
//  Description: Holds all information for a persistent object instance.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using Chili.Opf3.Concurrency;

namespace Chili.Opf3
{
	/// <summary>
	/// Implements all information that are required to remote or persist a persistent
	/// object to any kind of device.
	/// </summary>
	/// <remarks>
	/// 	<para>This class contains all information to save a persistent object to any kind
	///     of device. It makes the persistent object independent from the
	///     <see cref="Opf3.ObjectContext">ObjectContext</see>. This object is returned by the
	///     <see cref="Opf3.ISelfContainingObject">ISelfContaining</see> interface that is
	///     implemented if an object is remoted or persisted to a device (other then a
	///     storage). The interface should also be implemented if the object is loaded with one
	///     <see cref="Opf3.ObjectContext">ObjectContext</see> and then saved with
	///     another.</para>
	/// 	<para><b>Attention:</b> Please do not change any properties of this object from
	///     your code, since this could affect the stability of your application! If you
	///     implement some new kind of
	///     <see cref="Opf3.Concurrency.IConcurrencyManager">IConcurrencyManager</see> you have
	///     to populate the <see cref="Opf3.ObjectInfo.ConcurrencyData">ConcurrencyData</see>
	///     property with your concurrency data. Other properties are not required to
	///     modify.</para>
	/// </remarks>
	/// <example>
	/// 	<code lang="CS">
	/// [Persistent("USER")]
	/// public class User : ISelfContainingObject
	/// {
	///     // ... Other properties and fields.
	/// }
	///  
	/// // ... Other code
	///  
	/// // Loads a user object from the storage.
	/// ISelfContainingObject user = 
	///     (ISelfContainingObject)context.GetObject&lt;User&gt;("Id = {0}", "1");
	/// // Checks if the object has been loaded.
	/// if (user.ObjectInfo.ObjectState == ObjectStates.Loaded)
	/// {
	///     Debug.WriteLine("Object has been loaded.");
	/// }
	///     </code>
	/// </example>
	[Serializable]
	public sealed class ObjectInfo : ICloneable
	{
		private ObjectStates _objectState = ObjectStates.None;
		private ConcurrencyDataBase _concurrencyData;
		private bool _markForDeletion;
		private byte[] _checksum;

		/// <summary>
		/// Creates an instance of the <see cref="ObjectInfo">ObjectInfo Class</see>.
		/// </summary>
		public ObjectInfo()
		{ 
		}

		/// <summary>
		/// Specifies the <see cref="ObjectState">state</see> of the persistent object
		/// associated with this <see cref="ObjectInfo">ObjectInfo</see> instance.
		/// </summary>
		/// <value>
		/// Returns the <see cref="ObjectState">ObjectState</see> of the persistent
		/// object
		/// </value>
		public ObjectStates ObjectState
		{
			get { return _objectState; }
			set { _objectState = value; }
		}

		/// <summary>
		/// Contains the data for/of the 
		/// <see cref="Opf3.Concurrency.IConcurrencyManager">IConcurrencyManager</see>.
		/// It is not required to change this property from your application. Changing
		/// this property is only required from any kind of 
		/// <see cref="Opf3.Concurrency.IConcurrencyManager">IConcurrencyManager</see>.
		/// </summary>
		/// <example>
		///     The following example shows how to set the concurrency data, usually implemented in
		///     a IConcurrencyManager . 
		///     <code lang="CS">
		/// // Sets the concurrency data.
		/// private void SetConcurrencyData&lt;T&gt;(T obj, ConcurrencyManagerEventArgs e)
		/// {
		///     // Calculates the MD5 representation of the object.
		///     MD5 md5 = CalculateMd5(obj);
		///  
		///     // Create the ConcurrencyData object.
		///     Md5ConcurrencyData concurrencyData = new Md5ConcurrencyData();
		///     concurrencyData.Md5 = md5;
		///  
		///     // Attachs it to the ObjectInfo object.
		///     e.ObjectInfo.ConcurrencyData = concurrencyData;
		/// }
		///     </code>
		/// </example>
		/// <value>
		/// Returns the concurrency information for the
		/// <see cref="Opf3.Concurrency.IConcurrencyManager">IConcurrencyManager</see>.
		/// </value>
		public ConcurrencyDataBase ConcurrencyData
		{
			get { return _concurrencyData; }
			set { _concurrencyData = value; }
		}

		/// <summary>
		/// Specifies whether the associated persistent object is marked for deletion.
		/// Objects marked for deletion are deleted the next time they are
		/// <see cref="ObjectContext.PersistChanges{T}(T)">persisted</see>.
		/// </summary>
		/// <value>Returns true if the associated persistent object is marked for deletion.</value>
		public bool MarkForDeletion
		{
			get { return _markForDeletion; }
			set { _markForDeletion = value; }
		}

		/// <summary>
		/// Specifies the checksum of the associated persistent object. This sum is used
		/// to check if the object has been changed since being in memory.
		/// </summary>
		/// <returns>A byte array that contains the checksum for the persistent object.</returns>
		public byte[] Checksum
		{
			get { return _checksum; }
			set { _checksum = value; }
		}

		/// <summary>
		/// Restores the data of the <paramref name="ObjectInfo">ObjectInfo</paramref> object
		/// by using the data of the <paramref name="ObjectInfo">ObjectInfo</paramref> object
		/// passed.
		/// </summary>
		/// <param name="objectInfo">
		/// 	<see cref="ObjectInfo">ObjectInfo</see> object used to restored this
		/// instance.
		/// </param>
		internal void Restore(ObjectInfo objectInfo)
		{
			this.ConcurrencyData = objectInfo.ConcurrencyData;
			this.MarkForDeletion = objectInfo.MarkForDeletion;
			this.ObjectState = objectInfo.ObjectState;
			this.Checksum = objectInfo.Checksum;
		}

		#region ICloneable Members

		/// <summary>Creates a new object that is a copy of the current instance.</summary>
		/// <returns>A new object that is a copy of this instance.</returns>
		public object Clone()
		{
			// ATTENTION: When changing this method change also the Restore method. They are required for transaction management.
			ObjectInfo objectInfo = new ObjectInfo();

			if (_concurrencyData != null)
				objectInfo.ConcurrencyData = (ConcurrencyDataBase)_concurrencyData.Clone();
			objectInfo.MarkForDeletion = _markForDeletion;
			objectInfo.ObjectState = _objectState;
			if (_checksum != null)
				objectInfo.Checksum = (byte[])_checksum.Clone();
			
			return objectInfo;
		}

		#endregion
	}
}
